﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using System.IO;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Threading;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using BitcoinMarket.Resources;

namespace BitcoinMarket.DataSources
{
    public class BitcoinChartsMarketsDataSource
    {
        private string MarketsPublicUrl = "http://bitcoincharts.com/t/markets.json";
        private string MarketsLocalUrl = "http://bitcoincharts.com/t/markets.json";//"http://localhost/markets.json";

        private string PricesPublicUrl = "http://bitcoincharts.com/t/weighted_prices.json";
        private string PricesLocalUrl = "http://bitcoincharts.com/t/weighted_prices.json";//"http://localhost/weighted_prices.json";

        private string TradesPublicUrl = "http://bitcoincharts.com/t/trades.csv?symbol=%s&start=%st&end=%e";
        private string TradesLocalUrl = "http://localhost/trades_%s.csv";

        private string MarketsLocalDataFilename = "BitcoinChartsMarkets_data.json";
        private string PricesLocalDataFilename = "BitcoinChartsPrices_data.json";

        private JArray parsedMarketsData = null;
        private JObject parsedPricesData = null;
        private Collection<TradesData> parsedTradesData = new Collection<TradesData>();
        private Collection<string> MarketsTradesToDownload = null;

        //Webclient instance for downloading data
        private WebClient TradesWebclient;

        private Action<JArray, JObject> MarketsDownloadCallback = null;
        private Action<Collection<TradesData>> TradesDownloadCallback = null;

        private bool UseCache = false;

        /// <summary>
        /// Constructor
        /// </summary>
        public BitcoinChartsMarketsDataSource()
        {
            //Init and set the callback for the trades Webclient
            this.TradesWebclient = new WebClient();
            this.TradesWebclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(csv_TradesDownloadStringCompleted);
        }

        /// <summary>
        /// Constructor for getting the temp file last change date - no initialization is made if not requested
        /// </summary>
        public BitcoinChartsMarketsDataSource(bool DoNotInit)
        {
            if (!DoNotInit)
            {
                //Init and set the callback for the trades Webclient
                this.TradesWebclient = new WebClient();
                this.TradesWebclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(csv_TradesDownloadStringCompleted);
            }
        }

        public DateTime GetTMPFileLastChangeDate()
        {
            IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
            DateTimeOffset lastChange = myIsolatedStorage.GetLastWriteTime(this.MarketsLocalDataFilename);

            return lastChange.Date;
        }

        /// <summary>
        /// Markets JSON file downloaded callback
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void json_MarketsDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            string jsonstr = "";

            if (e == null || e.Error != null)
            {
                //Tell the user
                //MessageBox.Show("BitcoinCharts: Could not download new data. Data from the last successfull download will be used.");
                if (e != null)
                {
                    System.Diagnostics.Debug.WriteLine(e.Error.InnerException);
                }

                //Get last saved JSON data from a local file
                try
                {
                    IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                    IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(this.MarketsLocalDataFilename, FileMode.Open, FileAccess.Read);
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        jsonstr = reader.ReadToEnd();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(AppResources.BitcoinchartsNoData);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);

                    return;
                }
            }
            else
            {
                jsonstr = e.Result;

                //Save the downladed data into alocal file
                IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();

                try
                {
                    using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream(this.MarketsLocalDataFilename, FileMode.Create, FileAccess.Write, myIsolatedStorage)))
                    {
                        writeFile.Write(jsonstr);
                        writeFile.Close();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(AppResources.BitcoinchartsNoData);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);
                }
            }

            //Fix problems with exponents
            jsonstr = jsonstr.Replace("0E-", "0.0e-");

            //Parse JSON data into an array
            try
            {
                this.parsedMarketsData = JArray.Parse(jsonstr);

                if (this.parsedPricesData != null)
                {
                    this.MarketsDownloadCallback(this.parsedMarketsData, this.parsedPricesData);
                }
            }
            catch (FormatException ex)
            {
                MessageBox.Show(ex.Message);
                System.Diagnostics.Debug.WriteLine(ex.Data);
            }
            catch (JsonReaderException ex)
            {
                MessageBox.Show(ex.Message);
                System.Diagnostics.Debug.WriteLine(ex.InnerException);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                System.Diagnostics.Debug.WriteLine(ex.InnerException);
            }
        }

        /// <summary>
        /// Prices JSON file downloaded callback
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void json_PricesDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            string jsonstr = "";

            if (e == null || e.Error != null)
            {
                //Tell the user
                //MessageBox.Show("BitcoinCharts: Could not download new data. Data from the last successfull download will be used.");
                if (e != null)
                {
                    System.Diagnostics.Debug.WriteLine(e.Error.InnerException);
                }

                //Get last saved JSON data from a local file
                try
                {
                    IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
                    IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(this.PricesLocalDataFilename, FileMode.Open, FileAccess.Read);
                    using (StreamReader reader = new StreamReader(fileStream))
                    {
                        jsonstr = reader.ReadToEnd();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(AppResources.BitcoinchartsNoData);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);

                    return;
                }
            }
            else
            {
                jsonstr = e.Result;

                //Save the downladed data into alocal file
                IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();

                try
                {
                    using (StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream(this.PricesLocalDataFilename, FileMode.Create, FileAccess.Write, myIsolatedStorage)))
                    {
                        writeFile.Write(jsonstr);
                        writeFile.Close();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(AppResources.BitcoinchartsNoData);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);
                }
            }

            //Parse JSON data into an object
            try
            {
                this.parsedPricesData = JObject.Parse(jsonstr);

                if (this.parsedMarketsData != null)
                {
                    this.MarketsDownloadCallback(this.parsedMarketsData, this.parsedPricesData);
                }
            }
            catch (FormatException ex)
            {
                MessageBox.Show(ex.Message);
                System.Diagnostics.Debug.WriteLine(ex.Data);
            }
            catch (JsonReaderException ex)
            {
                MessageBox.Show(ex.Message);
                System.Diagnostics.Debug.WriteLine(ex.InnerException);
            }
        }

        /// <summary>
        /// Downloads the JSON files from the web
        /// </summary>
        /// <param name="MarketsDownloadCallback"></param>
        /// <param name="UseCache"></param>
        public void GetMarketsData(Action<JArray, JObject> MarketsDownloadCallback, bool UseCache)
        {
            //Set the download callbacks
            this.MarketsDownloadCallback = MarketsDownloadCallback;

            //Set cache param
            this.UseCache = UseCache;

            //Download the JSON data from the web or skip downloading if the cache is used
            if (!UseCache)
            {
                WebClient MarketsJSON = new WebClient();
                MarketsJSON.DownloadStringCompleted += new DownloadStringCompletedEventHandler(json_MarketsDownloadStringCompleted);

                WebClient PricesJSON = new WebClient();
                PricesJSON.DownloadStringCompleted += new DownloadStringCompletedEventHandler(json_PricesDownloadStringCompleted);

                try
                {
                    switch (Microsoft.Devices.Environment.DeviceType)
                    {
                        case Microsoft.Devices.DeviceType.Device:
                            MarketsJSON.DownloadStringAsync(new Uri(this.MarketsPublicUrl));
                            PricesJSON.DownloadStringAsync(new Uri(this.PricesPublicUrl));
                            break;
                        case Microsoft.Devices.DeviceType.Emulator:
                            MarketsJSON.DownloadStringAsync(new Uri(this.MarketsLocalUrl));
                            PricesJSON.DownloadStringAsync(new Uri(this.PricesLocalUrl));
                            break;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);
                }
            }
            else
            {
                json_MarketsDownloadStringCompleted(null, null);
                json_PricesDownloadStringCompleted(null, null);
            }
        }

        /// <summary>
        /// Trades CSV file downloaded callback
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void csv_TradesDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            string csvstr = "";
            string CurrentMarket = (string)e.UserState;

            if (e.Error != null)
            {
                //Tell the user
                //MessageBox.Show("Could not download trades data! Please check your internet connection.");
                System.Diagnostics.Debug.WriteLine("Download error: " + e.Error.Message);
                //MessageBox.Show("Download error: " + e.Error.Message);
                //return;
            }
            else
            {
                csvstr = e.Result;


                //Parse CSV data into an array
                try
                {
                    TradesData tmpData = new TradesData();
                    tmpData.MarketName = CurrentMarket;
                    tmpData.Data = new Collection<string[]>();

                    var result = Regex.Split(csvstr, "\r\n|\r|\n");

                    foreach (string line in result)
                    {
                        string[] tmp = line.Split(',');

                        tmpData.Data.Add(tmp);

                        //tmp = Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
                    }

                    this.parsedTradesData.Add(tmpData);
                }
                catch (FormatException ex)
                {
                    MessageBox.Show(ex.Message);
                    System.Diagnostics.Debug.WriteLine(ex.Data);
                }
                catch (JsonReaderException ex)
                {
                    MessageBox.Show(ex.Message);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);
                }
            }

            //Download the next file
            this.MarketsTradesToDownload.RemoveAt(0);
            
            if (this.MarketsTradesToDownload.Count > 0)
            {
                foreach (string MarketName in this.MarketsTradesToDownload)
                {
                    try
                    {
                        switch (Microsoft.Devices.Environment.DeviceType)
                        {
                            case Microsoft.Devices.DeviceType.Device:
                                this.TradesWebclient.DownloadStringAsync(new Uri(this.TradesPublicUrl.Replace("%s", MarketName)), MarketName);
                                break;
                            case Microsoft.Devices.DeviceType.Emulator:
                                this.TradesWebclient.DownloadStringAsync(new Uri(this.TradesLocalUrl.Replace("%s", MarketName)), MarketName);
                                break;
                        }

                        //We need just the forst item
                        break;
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                        System.Diagnostics.Debug.WriteLine(ex.InnerException);
                    }
                }
            }
            else //If all downloads were finished pass the data back
            {
                this.TradesDownloadCallback(this.parsedTradesData);
            }
        }

        /// <summary>
        /// Downloads the Trades history CSV files from the web
        /// </summary>
        public void GetTradesData(Collection<string> MarketNames, Action<Collection<TradesData>> TradesDownloadCallback)
        {
            //Set the download callbacks
            this.TradesDownloadCallback = TradesDownloadCallback;
            this.MarketsTradesToDownload = MarketNames;

            //Download the CSV data from the web
            foreach (string MarketName in MarketNames)
            {
                try
                {
                    int endTime = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
                    int startTime = endTime - 3 * 24 * 60 * 60; //3 days in seconds
                    string url;

                    switch (Microsoft.Devices.Environment.DeviceType)
                    {
                        case Microsoft.Devices.DeviceType.Device:
                            url = this.TradesPublicUrl
                                                .Replace("%s", MarketName)
                                                .Replace("%st", startTime.ToString())
                                                .Replace("%e", endTime.ToString());
                            this.TradesWebclient.DownloadStringAsync(new Uri(url), MarketName);
                            break;
                        case Microsoft.Devices.DeviceType.Emulator:
                            url = this.TradesPublicUrl
                                                .Replace("%s", MarketName)
                                                .Replace("%st", startTime.ToString())
                                                .Replace("%e", endTime.ToString());
                            this.TradesWebclient.DownloadStringAsync(new Uri(url), MarketName);
                            break;
                    }

                    //We need just the first item
                    break;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    System.Diagnostics.Debug.WriteLine(ex.InnerException);
                }
            }
        }
    }

    /// <summary>
    /// Object containing the parsed data about trades
    /// </summary>
    public class TradesData
    {
        public string MarketName { get; set; }
        public Collection<string[]> Data { get; set; }
    }
}
